package org.tinygroup.bizframe.aop.interceptor;

import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.springframework.aop.support.AopUtils;
import org.tinygroup.bizframe.aop.config.base.ExtensionAction;
import org.tinygroup.bizframe.aop.processor.base.ExtensionActionProcessor;
import org.tinygroup.bizframe.aop.resolver.ExtensionActionResolver;
import org.tinygroup.commons.tools.Assert;
import org.tinygroup.commons.tools.CollectionUtil;

public class ExtensionInterceptor implements MethodInterceptor {

    private Map<Class<? extends ExtensionActionProcessor>, ExtensionActionProcessor> processorMap = new ConcurrentHashMap<Class<? extends ExtensionActionProcessor>, ExtensionActionProcessor>();

    private List<ExtensionActionResolver> actionResolverList = new ArrayList<ExtensionActionResolver>();

    public void addActionResolver(ExtensionActionResolver resolver) {
        actionResolverList.add(resolver);
    }

    public void addProcessor(ExtensionActionProcessor extensionActionProcessor) {
        processorMap.put(extensionActionProcessor.getClass(), extensionActionProcessor);
    }

    @Override
    public Object invoke(MethodInvocation invocation) throws Throwable {
        Method method = invocation.getMethod();
        Object target = invocation.getThis();
        if (AopUtils.isToStringMethod(method)) {
            // Allow for differentiating between the proxy and the raw Connection.
            StringBuffer buf = new StringBuffer("Proxy for foundation extension");
            if (target != null) {
                buf.append("[").append(target.toString()).append("]");
            }
            return buf.toString();
        }
        List<ExtensionAction> extensionActions = resolveExtensionAction(method);
        if (CollectionUtil.isEmpty(extensionActions)) {
            return invocation.proceed();
        }
        List<ExtensionHolder> executionChain = createExecutionChain(extensionActions);

        if (!CollectionUtil.isEmpty(executionChain)) {
            for (ExtensionHolder holder : executionChain) {
                ExtensionActionProcessor processor = holder.getProcessor();
                ExtensionAction action = holder.getAction();
                if (!processor.preProcess(action, invocation)) {//前置处理
                    return processor.endProcessor(action, invocation);
                }
            }
        }
        Object result = invocation.proceed();
        if (!CollectionUtil.isEmpty(executionChain)) {//后置处理
            for (int i = executionChain.size() - 1; i >= 0; i--) {
                ExtensionHolder holder = executionChain.get(i);
                ExtensionAction action = holder.getAction();
                ExtensionActionProcessor processor = holder.getProcessor();
                processor.postProcess(action, invocation, result);
            }
        }
        return result;
    }

    /**
     * 针对某个方法上的所有扩展动作构建执行链
     *
     * @param actions 某个方法上的所有动作
     * @return
     */
    private List<ExtensionHolder> createExecutionChain(List<ExtensionAction> actions) {
        List<ExtensionHolder> executionChain = new ArrayList<ExtensionHolder>();
        for (ExtensionAction action : actions) {
            ExtensionActionProcessor extensionActionProcessor = processorMap.get(action.getProcessorClass());
            Assert.assertNotNull(extensionActionProcessor,
                    String.format("未注册扩展操作类型[%s]对应的扩展动作处理器",
                            action.getProcessorClass().getName()));
            ExtensionHolder holder = new ExtensionHolder(action, extensionActionProcessor);
            executionChain.add(holder);
        }
        return executionChain;
    }

    /**
     * resolve ExtensionAction By ActionResolverList
     *
     * @param method
     * @return
     */
    private List<ExtensionAction> resolveExtensionAction(Method method) {
        for (ExtensionActionResolver extensionActionResolver : actionResolverList) {
            List<ExtensionAction> extensionActionList = extensionActionResolver.resolve(method);
            if (!CollectionUtil.isEmpty(extensionActionList)) {
                return extensionActionList;
            }
        }
        return null;
    }
}
